package cn.iocoder.yudao.module.trade.order.service.calculator;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.api.member.level.MemberLevelApi;
import cn.iocoder.yudao.module.api.member.level.dto.MemberLevelRespDTO;
import cn.iocoder.yudao.module.api.promotion.activity.enums.PromotionDiscountTypeEnum;
import cn.iocoder.yudao.module.api.promotion.activity.enums.PromotionTypeEnum;
import cn.iocoder.yudao.module.api.promotion.discount.DiscountActivityApi;
import cn.iocoder.yudao.module.api.promotion.discount.dto.DiscountProductRespDTO;
import cn.iocoder.yudao.module.api.trade.order.enums.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.order.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.order.bo.TradePriceCalculateRespBO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.calculateRatePrice;
import static cn.iocoder.yudao.module.trade.order.service.calculator.TradePriceCalculatorHelper.formatPrice;

/**
 * 限时折扣的 {@link TradePriceCalculator} 实现类
 * 由于“用户折扣”和“限时折扣”是冲突，需要选择优惠金额多的，所以也放在这里计算
 *
 * @author 芋道源码
 */
@Slf4j
@Component
@Order(TradePriceCalculator.ORDER_DISCOUNT_ACTIVITY)
public class TradeDiscountActivityAndMemberLevelPriceCalculator implements TradePriceCalculator {

    @Resource
    private DiscountActivityApi discountActivityApi;
    @Resource
    private MemberLevelApi memberLevelApi;

    @Override
    public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
        // 0. 非优惠活动订单，才计算该优惠
        if (Arrays.asList(TradeOrderTypeEnum.SECKILL.getType(), TradeOrderTypeEnum.BARGAIN.getType(),
                TradeOrderTypeEnum.COMBINATION.getType(), TradeOrderTypeEnum.POINT.getType(),
                TradeOrderTypeEnum.GROUPON.getType()).contains(result.getType())) {
            log.debug("[calculate][只有非优惠活动订单({})，才处理限时折扣优惠]", result.getType());
            return;
        }

        List<TradePriceCalculateRespBO.OrderItem> orderItems = result.getItems();
        // 1.1 获得 SKU 对应的限时折扣活动
        List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductListBySkuIds(
                convertSet(orderItems, TradePriceCalculateRespBO.OrderItem::getSkuId));
        Map<Long, DiscountProductRespDTO> discountProductMap = convertMap(discountProducts, DiscountProductRespDTO::getSkuId);
        // 1.2 获得用户等级
        MemberLevelRespDTO level = memberLevelApi.getMemberLevelByUserId(param.getUserId());

        // 2. 计算每个 SKU 的优惠金额
        boolean flag = false;
        for (TradePriceCalculateRespBO.OrderItem orderItem : orderItems) {
            if (!orderItem.getSelected()) {
                log.debug("[calculate][SKU({}) 没有选中，不处理限时折扣优惠]", orderItem.getSkuId());
                continue;
            }
            // 2.1 计算限时折扣的优惠金额
            DiscountProductRespDTO discountProduct = discountProductMap.get(orderItem.getSkuId());
            Integer discountPrice = calculateActivityPrice(discountProduct, orderItem);
            // 2.2 计算用户等级优惠金额
            int memberLevelPrice = calculateMemberLevelPrice(level, orderItem);
            if (discountPrice <= 0 && memberLevelPrice <= 0) {
                log.debug("[calculate][SKU({}) 没有限时折扣优惠，也没有用户等级优惠]", orderItem.getSkuId());
                continue;
            }

            // 3. 选择优惠金额多的
            flag = true;
            if (discountPrice > memberLevelPrice) {
                log.debug("[calculate][SKU({}) 限时折扣优惠({}) 大于用户等级优惠({})，优先使用限时折扣优惠]",
                        orderItem.getSkuId(), discountProduct.getActivityId(), level.getId());
                // 更新 SKU 优惠金额，在其他优惠金额基础上再优惠
                orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice);
            } else {
                log.debug("[calculate][SKU({}) 限时折扣优惠({}) 小于用户等级优惠({})，优先使用用户等级优惠]",
                        orderItem.getSkuId(), discountProduct.getActivityId(), level.getId());
                // 更新 SKU 的优惠金额
                orderItem.setMemberLevelPrice(memberLevelPrice);
            }

            // 4. 分摊优惠
            TradePriceCalculatorHelper.recountPayPrice(orderItem);
            TradePriceCalculatorHelper.recountAllPrice(result);
        }
        if (flag) {
            List<Integer> discountPrices = orderItems.stream().map(TradePriceCalculateRespBO.OrderItem::getDiscountPrice).toList();
            if (CollUtil.isNotEmpty(discountPrices)) {
                String activityNames = discountProducts.stream().map(DiscountProductRespDTO::getActivityName).collect(Collectors.joining("、"));
                TradePriceCalculatorHelper.addPromotion(result, orderItems,
                        0L, activityNames, PromotionTypeEnum.DISCOUNT_ACTIVITY.getValue(),
                        StrUtil.format("限时折扣：省 {} 元", formatPrice(discountPrices.stream().reduce(Integer::sum).orElse(0))),
                        discountPrices);
                log.debug("[calculate][限时折扣价格计算完成，使用限时折扣优惠] {}", result.getPrice());
            }
            List<Integer> memberLevelPrices = orderItems.stream().map(TradePriceCalculateRespBO.OrderItem::getMemberLevelPrice).toList();
            if (CollUtil.isNotEmpty(memberLevelPrices)) {
                TradePriceCalculatorHelper.addPromotion(result, orderItems,
                        level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getValue(),
                        String.format("用户等级折扣：省 %s 元", formatPrice(memberLevelPrices.stream().reduce(Integer::sum).orElse(0))),
                        memberLevelPrices);
            }
        }
        log.debug("[calculate][限时折扣({})价格计算完成，使用用户等级优惠] {}", flag, result.getPrice());
    }

    /**
     * 计算优惠活动的减免价格
     *
     * @param discount 优惠活动
     * @param orderItem 交易项
     * @return 优惠价格
     */
    public Integer calculateActivityPrice(DiscountProductRespDTO discount,
                                          TradePriceCalculateRespBO.OrderItem orderItem) {
        if (discount == null) {
            return 0;
        }
        Integer newPrice = orderItem.getPayPrice();
        if (PromotionDiscountTypeEnum.PRICE.getType().equals(discount.getDiscountType())) { // 减价
            newPrice -= discount.getDiscountPrice() * orderItem.getCount();
        } else if (PromotionDiscountTypeEnum.PERCENT.getType().equals(discount.getDiscountType())) { // 打折
            newPrice = calculateRatePrice(orderItem.getPayPrice(), discount.getDiscountPercent() / 100.0);
        } else {
            throw new IllegalArgumentException(String.format("优惠活动的产品(%s) 的优惠类型不正确", discount));
        }
        return orderItem.getPayPrice() - newPrice;
    }

    /**
     * 计算用户等级尊享价格
     *
     * @param level 用户等级
     * @param orderItem 交易项
     * @return 优惠价格
     */
    public Integer calculateMemberLevelPrice(MemberLevelRespDTO level,
                                             TradePriceCalculateRespBO.OrderItem orderItem) {
        if (level == null) {
            log.debug("[calculateMemberLevelPrice][用户等级为空，不处理用户等级优惠]");
            return 0;
        }
        if (CommonStatusEnum.isDisable(level.getStatus())) {
            log.debug("[calculate][用户所属等级({})已被禁用，不处理用户等级优惠]", level.getName());
            return 0;
        }
        if (level.getDiscountPercent() == null || level.getDiscountPercent() >= 100 || level.getDiscountPercent() <= 0) {
            log.debug("[calculate][用户所属等级({})折扣({})无效，不处理用户等级优惠]", level.getName(), level.getDiscountPercent());
            return 0;
        }
        Integer newPrice = calculateRatePrice(orderItem.getPayPrice(), level.getDiscountPercent().doubleValue());
        return orderItem.getPayPrice() - newPrice;
    }

}
